home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Source.bin / ScrollingText.java < prev    next >
Text File  |  1998-08-21  |  28KB  |  1,044 lines

  1. package symantec.itools.multimedia;
  2.  
  3. import java.awt.*;
  4. import java.awt.event.*;
  5. import java.applet.Applet;
  6. import java.applet.AppletContext;
  7. import java.net.URL;
  8. import java.beans.PropertyVetoException;
  9. import java.beans.PropertyChangeListener;
  10. import java.beans.VetoableChangeListener;
  11. import java.util.ResourceBundle;
  12.  
  13. /**
  14.  * Scrolling text component.  Forms a text banner that scrolls specified
  15.  * text horizontally.  The component allows multiple messages and links.
  16.  *
  17.  * @version 1.0, Feb 2, 1997
  18.  * @author Symantec
  19.  */
  20.  
  21. //     02/02/97    RKM    Checked it in
  22. //     02/07/97    RKM    Fixed bug in paint where the font was noit being set up in the offscreen image (thanks Dave)
  23. //    05/31/97    RKM    Updated to support Java 1.1
  24. //                    Converted events
  25. //                    Made properties bound & constrained
  26. //                    Made choice to have a little more code in saving old & new properties when firing, but
  27. //                    think the code is cleaner
  28. //                    Changed updateCurrentMessage & nextPos to protected
  29. //  07/17/97    CAR marked fields transient as needed
  30. //  07/17/97    CAR changed class Mouse to implement java.io.Serializable interface
  31. //  07/17/97    CAR initialized messageList in default constructor
  32. //  08/21/97    LAB Made paint use the same image unless the size changes ala ButtonBase
  33. //                    (faster, and fixes a Win bug).
  34.  
  35. public class ScrollingText extends Canvas implements Runnable
  36. {
  37.     // Enums
  38.  
  39.     /**
  40.      * Constant which indicates that the banner should scroll from right to left
  41.      */
  42.  
  43.     public static final int SCROLL_LEFT = 0;
  44.  
  45.     /**
  46.      * Constant which indicates that the banner should scroll from left to right
  47.      */
  48.  
  49.     public static final int SCROLL_RIGHT = 1;
  50.  
  51.     // Property variables
  52.  
  53.     /**
  54.      * Current scroll direction. Default value SCROLL_LEFT.
  55.      */
  56.     protected int scrollDirection;
  57.  
  58.     /**
  59.      * Distance to scroll on each update.  Distance is in pixels. Default value 10.
  60.      */
  61.     protected int scrollUnit;                            //the incremental scrolling distance in pixels
  62.  
  63.     /**
  64.      * Controls the speed of the scroll. Default value 150.
  65.      */
  66.     protected int sleepTime;                            //controls the speed of the scroll
  67.  
  68.     /**
  69.      * Color for highlighted text. Default value is red.
  70.      */
  71.  
  72.     protected Color hiliteColor;
  73.  
  74.     /**
  75.      * List of messages to scroll across the banner.
  76.      */
  77.     protected String[] messageList;
  78.  
  79.  
  80.     /**
  81.      * URL links which correspond with messages.
  82.      */
  83.     protected URL[] linkToList;
  84.  
  85.     /**
  86.      * Location in browser to show linked HTML pages.
  87.      * Valid values are "_self" to show in the current frame;
  88.      * "_parent" show in the parent frame;
  89.      * "_top" show in the topmost frame;
  90.      * "_blank" show in a new unnamed top-level window;
  91.      * name show in a new top-level window named name.
  92.      */
  93.     protected String frame;
  94.  
  95.     // Internal variables
  96.  
  97.     /**
  98.      * If used in an applet, this is the applet's context.
  99.      */
  100.     transient protected AppletContext context;
  101.  
  102.     /**
  103.      * Thread which runs the scrolling animation.
  104.      */
  105.     transient protected Thread scrollThread = null;
  106.  
  107.     /**
  108.      * State of scrolling animation.
  109.      */
  110.     transient protected boolean suspended = false;
  111.  
  112.     /**
  113.      * Current horizontal text position.
  114.      */
  115.     transient protected int textX;
  116.  
  117.     /**
  118.      * Current vertical text position.
  119.      */
  120.     transient protected int textY;
  121.  
  122.     /**
  123.      * Width of current message text.
  124.      */
  125.     transient protected int textWidth;
  126.  
  127.     /**
  128.      * Height of current message text.
  129.      */
  130.     transient protected int textHeight;
  131.  
  132.  
  133.     /**
  134.      * Mouse over message text.
  135.      */
  136.     transient protected boolean isMouseOver = false;
  137.  
  138.     /**
  139.      * Last mouse cursor position.
  140.      */
  141.     transient protected int lastMouseX,lastMouseY;
  142.  
  143.     /**
  144.      * Status of mouse position.
  145.      */
  146.     transient protected boolean wasMouseOverText = false;
  147.  
  148.     /**
  149.      * Previous status message.
  150.      */
  151.     transient protected String wasStatusMessage = "";
  152.  
  153.  
  154.     /**
  155.      * Current message text.
  156.      */
  157.     protected String currMessage;
  158.  
  159.     /**
  160.      * Current URL link.
  161.      */
  162.     protected URL currLinkTo;
  163.  
  164.     /**
  165.      * Index of current message text and URL
  166.      */
  167.     protected int currIndex;
  168.  
  169.     // Constructor
  170.  
  171.     /**
  172.      * Construct default scrolling text banner.
  173.      */
  174.  
  175.     public ScrollingText()
  176.     {
  177.         super();
  178.  
  179.         //Set defaults for properties
  180.         scrollDirection = SCROLL_LEFT;
  181.         scrollUnit = 10;
  182.         sleepTime = 150;
  183.         hiliteColor = Color.red;
  184.         //messageList = new String[0];
  185.         ResourceBundle res = ResourceBundle.getBundle("symantec.itools.resources.ResBundle");
  186.  
  187.         messageList = new String[5];
  188.         messageList[0] = res.getString("put");
  189.         messageList[1] = res.getString("some");
  190.         messageList[2] = res.getString("text");
  191.         messageList[3] = res.getString("in");
  192.         messageList[4] = res.getString("here");
  193.         linkToList = new URL[0];
  194.         frame = null;
  195.  
  196.         //Init current message variables
  197.         currMessage = "";
  198.         currLinkTo = null;
  199.         currIndex = -1;
  200.  
  201.         vetos = new symantec.itools.beans.VetoableChangeSupport(this);
  202.         changes = new symantec.itools.beans.PropertyChangeSupport(this);
  203.     }
  204.  
  205.     //
  206.     // Getters/Setters
  207.     //
  208.  
  209.     /**
  210.      * Set sleep time between scroll steps.  This function controls
  211.      * the speed of scrolling.  A lower number indicates a faster speed.
  212.      *
  213.      * @param speed number of milliseconds to sleep between scroll steps.  The smaller the
  214.      *              number the faster the scroll.  Minimum value of 30.
  215.      *
  216.      * @exception PropertyVetoException
  217.      * if the specified property value is unacceptable
  218.      */
  219.     public void setScrollInterval(int speed)
  220.         throws PropertyVetoException
  221.     {
  222.         //???RKM??? Would an exception be better here
  223.         if (speed < 30)
  224.             speed = 30;
  225.  
  226.         if (speed != sleepTime)
  227.         {
  228.             Integer oldScrollIntervalInt = new Integer(sleepTime);
  229.             Integer newScrollIntervalInt = new Integer(speed);
  230.  
  231.             vetos.fireVetoableChange("ScrollInterval", oldScrollIntervalInt, newScrollIntervalInt);
  232.  
  233.             sleepTime = speed;
  234.  
  235.             changes.firePropertyChange("ScrollInterval", oldScrollIntervalInt, newScrollIntervalInt);
  236.  
  237.         }
  238.     }
  239.  
  240.     /**
  241.      * Obtain the current scroll interval.  This is the number of milliseconds
  242.      * that the scroll thread will sleep between scroll steps.
  243.      *
  244.      * @return current scroll delay setting, in milliseconds
  245.      */
  246.     public int getScrollInterval()
  247.     {
  248.         return sleepTime;
  249.     }
  250.  
  251.     /**
  252.      * Specify the size of each scroll step.
  253.      *
  254.      * @param unit the number of pixels to move the text on each scroll step
  255.      *
  256.      * @exception PropertyVetoException
  257.      * if the specified property value is unacceptable
  258.      */
  259.     public void setScrollUnit(int unit)
  260.         throws PropertyVetoException
  261.     {
  262.         //???RKM??? Would an exception be better here
  263.         if (unit < 1)
  264.             unit = 1;
  265.  
  266.         if (scrollUnit != unit)
  267.         {
  268.             Integer oldScrollUnitInt = new Integer(scrollUnit);
  269.             Integer newScrollUnitInt = new Integer(unit);
  270.  
  271.             vetos.fireVetoableChange("ScrollUnit", oldScrollUnitInt, newScrollUnitInt);
  272.  
  273.             scrollUnit = unit;
  274.  
  275.             changes.firePropertyChange("ScrollUnit", oldScrollUnitInt, newScrollUnitInt);
  276.         }
  277.     }
  278.  
  279.     /**
  280.      * Obtain the current size of the scroll step.
  281.      *
  282.      * @return the number of pixels that text moves on each scroll step
  283.      *
  284.      */
  285.     public int getScrollUnit()
  286.     {
  287.         return scrollUnit;
  288.     }
  289.  
  290.     /**
  291.      * Specify the banner's current scroll direction.
  292.      *
  293.      * @param dir direction to scroll. Valid values are SCROLL_LEFT and SCROLL_RIGHT
  294.      *
  295.      * @exception PropertyVetoException
  296.      * if the specified property value is unacceptable
  297.      */
  298.     public void setScrollDirection(int dir)
  299.         throws PropertyVetoException
  300.     {
  301.         //???RKM??? Would an exception be better here
  302.         if (dir != SCROLL_LEFT && dir != SCROLL_RIGHT)
  303.             dir = SCROLL_RIGHT;
  304.  
  305.         if (scrollDirection != dir)
  306.         {
  307.             Integer oldScrollDirectionInt = new Integer(scrollDirection);
  308.             Integer newScrollDirectionInt = new Integer(dir);
  309.  
  310.             vetos.fireVetoableChange("ScrollDirection", oldScrollDirectionInt, newScrollDirectionInt);
  311.  
  312.             scrollDirection = dir;
  313.  
  314.             changes.firePropertyChange("ScrollDirection", oldScrollDirectionInt, newScrollDirectionInt);
  315.         }
  316.     }
  317.  
  318.     /**
  319.      * Obtain the current direction of scrolling.
  320.      *
  321.      * @return the current direction, either SCROLL_LEFT or SCROLL_RIGHT
  322.      */
  323.     public int getScrollDirection()
  324.     {
  325.         return scrollDirection;
  326.     }
  327.  
  328.     /**
  329.      * Set the highlight color for text.  Text is highlighted in this color when the mouse cursor is over
  330.      * it and it contains a non-null link.
  331.      *
  332.      * @param newHiliteColor color for highlighted text.
  333.      *
  334.      * @exception PropertyVetoException
  335.      * if the specified property value is unacceptable
  336.      */
  337.     public void setHiliteColor(Color newHiliteColor)
  338.         throws PropertyVetoException
  339.     {
  340.         if (!hiliteColor.equals(newHiliteColor))
  341.         {
  342.             Color oldColor = hiliteColor;
  343.             Color newColor = newHiliteColor;
  344.  
  345.             vetos.fireVetoableChange("HiliteColor", oldColor, newColor);
  346.  
  347.             hiliteColor = newHiliteColor;
  348.  
  349.             changes.firePropertyChange("HiliteColor", oldColor, newColor);
  350.         }
  351.     }
  352.  
  353.     /**
  354.      * Obtain the current color for text highlighting
  355.      *
  356.      * @return current highlight color
  357.      */
  358.  
  359.     public Color getHiliteColor()
  360.     {
  361.         return hiliteColor;
  362.     }
  363.  
  364.     /**
  365.      * Specify the current list of messages.  These messages scroll sequentially
  366.      * through the banner.
  367.      *
  368.      * @param list list of messages to display
  369.      *
  370.      * @exception PropertyVetoException
  371.      * if the specified property value is unacceptable
  372.      */
  373.     public void setMessageList(String[] list)
  374.         throws PropertyVetoException
  375.     {
  376.         if (messageList != list)
  377.         {
  378.             String[] oldMessageList = messageList;
  379.             String[] newMessageList = list;
  380.  
  381.             currIndex = 0;
  382.             vetos.fireVetoableChange("MessageList",oldMessageList,newMessageList);
  383.  
  384.             messageList = list;
  385.  
  386.             changes.firePropertyChange("MessageList",oldMessageList,newMessageList);
  387.  
  388.             updateCurrentMessage(false);
  389.         }
  390.     }
  391.  
  392.     /**
  393.      * Obtain the current list of messages.
  394.      *
  395.      * @return the current list of messages being displayed
  396.      */
  397.     public String[] getMessageList()
  398.     {
  399.         return messageList;
  400.     }
  401.  
  402.     /**
  403.      * Specify the list of URLs for message links.
  404.      * Each link corresponds with a message in the message list.
  405.      * If a message has no link, a null URL should be included for that message.
  406.      *
  407.      * @param list list of URL links
  408.      *
  409.      * @exception PropertyVetoException
  410.      * if the specified property value is unacceptable
  411.      */
  412.     public void setLinkToList(URL[] list)
  413.         throws PropertyVetoException
  414.     {
  415.         if (linkToList != list)
  416.         {
  417.             URL[] oldLinkToList = linkToList;
  418.             URL[] newLinkToList = list;
  419.  
  420.             currIndex = 0;
  421.             vetos.fireVetoableChange("LinkToList",oldLinkToList,newLinkToList);
  422.  
  423.             linkToList = list;
  424.  
  425.             changes.firePropertyChange("LinkToList",oldLinkToList,newLinkToList);
  426.  
  427.             updateCurrentMessage(false);
  428.         }
  429.     }
  430.  
  431.     /**
  432.      * Obtain the current set of URL links.
  433.      *
  434.      * @return list of URL links.  Each URL corresponds to the message of the same index in the message list
  435.      */
  436.     public URL[] getLinkToList()
  437.     {
  438.         return linkToList;
  439.     }
  440.  
  441.     /**
  442.      * Specify the display location of linked pages.  If the ScrollingText is in an applet this
  443.      * method determines where the linked pages are displayed.
  444.      *
  445.      * @param newFrame where to display the linked documents.
  446.      * Valid values are "_self" to show in the current frame;
  447.      * "_parent" show in the parent frame;
  448.      * "_top" show in the topmost frame;
  449.      * "_blank" show in a new unnamed top-level window;
  450.      * <name> show in a new top-level window named name
  451.      *
  452.      * @exception PropertyVetoException
  453.      * if the specified property value is unacceptable
  454.      */
  455.     public void setFrame(String newFrame)
  456.         throws PropertyVetoException
  457.     {
  458.         if (!symantec.itools.util.GeneralUtils.objectsEqual(frame,newFrame))
  459.         {
  460.             String oldFrame = frame;
  461.  
  462.             vetos.fireVetoableChange("Frame",oldFrame,newFrame);
  463.  
  464.             frame = newFrame;
  465.  
  466.             changes.firePropertyChange("Frame",newFrame,newFrame);
  467.         }
  468.     }
  469.  
  470.     /**
  471.      * Obtain the display location of linked pages.  If the ScrollingText is in an applet this
  472.      * method returns where the linked pages are displayed.
  473.      *
  474.      * @return Place to display the linked documents.
  475.      * Valid values are "_self" to show in the current frame;
  476.      * "_parent" show in the parent frame;
  477.      * "_top" show in the topmost frame;
  478.      * "_blank" show in a new unnamed top-level window;
  479.      * <name> show in a new top-level window named name
  480.      */
  481.     public String getFrame()
  482.     {
  483.         return frame;
  484.     }
  485.  
  486.     // Overridden methods
  487.  
  488.     /**
  489.      * Tells this component that it has been added to a container.
  490.      * This is a standard Java AWT method which gets called by the AWT when
  491.      * this component is added to a container. Typically, it is used to
  492.      * create this component's peer.
  493.      *
  494.      * It has been overridden here to start the scrolling text thread.
  495.      *
  496.      * @see #removeNotify
  497.      */
  498.     public synchronized void addNotify()
  499.     {
  500.         super.addNotify();
  501.  
  502.         //Hook up listeners
  503.         if (mouse == null)
  504.         {
  505.             mouse = new Mouse();
  506.             addMouseListener(mouse);
  507.         }
  508.         if (mouseMotion == null)
  509.         {
  510.             mouseMotion = new MouseMotion();
  511.             addMouseMotionListener(mouseMotion);
  512.         }
  513.  
  514.         //Start thread
  515.         scrollThread = new Thread(this);
  516.         scrollThread.setPriority(Thread.MIN_PRIORITY);
  517.         scrollThread.start();
  518.     }
  519.  
  520.     /**
  521.      * Tells this component that it is being removed from a container.
  522.      * This is a standard Java AWT method which gets called by the AWT when
  523.      * this component is removed from a container. Typically, it is used to
  524.      * destroy the peers of this component and all its subcomponents.
  525.      *
  526.      * It has been overridden here to stop the scrolling text thread.
  527.      *
  528.      * @see #addNotify
  529.      */
  530.     public synchronized void removeNotify()
  531.     {
  532.         //Stop thread
  533.         if (scrollThread != null)
  534.         {
  535.             scrollThread.stop();
  536.             scrollThread = null;
  537.         }
  538.  
  539.         //Unhook listeners
  540.         if (mouse != null)
  541.         {
  542.             removeMouseListener(mouse);
  543.             mouse = null;
  544.         }
  545.         if (mouseMotion != null)
  546.         {
  547.             removeMouseMotionListener(mouseMotion);
  548.             mouseMotion = null;
  549.         }
  550.  
  551.         super.removeNotify();
  552.     }
  553.  
  554.     /**
  555.      * Resumes the animation of the scrolling text.
  556.      */
  557.     public void startScrollingText() {
  558.         suspended = false;
  559.         show();
  560.     }
  561.  
  562.     /**
  563.      * Suspends the animation of the scrolling text.
  564.      */
  565.     public void stopScrollingText() {
  566.         suspended = true;
  567.     }
  568.  
  569.     /**
  570.      * Makes this component visible.
  571.      * This is a standard Java AWT method which gets called to show this
  572.      * component. If this component was invisible due to a previous hide()
  573.      * call it make this component visible again.
  574.      *
  575.      * @see #hide
  576.      */
  577.     public synchronized void show() {
  578.         super.show();
  579.         if (isVisible()) {
  580.             if (scrollThread != null) {
  581.                 scrollThread.setPriority(Thread.MAX_PRIORITY);
  582.                 scrollThread.resume();
  583.             }
  584.         }
  585.     }
  586.  
  587.     /**
  588.      * Makes this component invisible.
  589.      * This is a standard Java AWT method which gets called to hide
  590.      * this component. A hidden component cannot be seen by the user nor
  591.      * does it take up space in its container, but it does continue to
  592.      * exist.
  593.      *
  594.      * @see #show
  595.      */
  596.     public synchronized void hide() {
  597.          super.hide();
  598.          if (!isVisible()) {
  599.              if (scrollThread != null)
  600.                 scrollThread.suspend();
  601.          }
  602.     }
  603.  
  604.     /**
  605.      * ScrollingText thread body.  This method is called by the Java virtual
  606.      * machine to when this thread starts.
  607.      */
  608.     public void run()
  609.     {
  610.         createTextParams();
  611.         while (true)
  612.         {
  613.             if (!suspended)
  614.             {
  615.                 nextPos();
  616.                 try
  617.                 {
  618.                     Thread.sleep(sleepTime);
  619.                 }
  620.                 catch(Exception e)
  621.                 {
  622.                 }
  623.             }
  624.         }
  625.     }
  626.  
  627.     /**
  628.      * Move to next message in message list.  Not usually called directly.
  629.      *
  630.      * @param next if true, move to next message; otherwise reset message with current index
  631.      */
  632.     protected void updateCurrentMessage(boolean next)
  633.     {
  634.         //Increase currIndex, try to get message out
  635.  
  636.         try
  637.         {
  638.             if (next)
  639.                 currIndex++;
  640.  
  641.             if(currIndex >= messageList.length)
  642.             {
  643.                 currIndex = 0;
  644.             }
  645.             
  646.             currMessage = messageList[currIndex];
  647.         }
  648.         catch(ArrayIndexOutOfBoundsException e)
  649.         {
  650.             //Index is out of range, reset to zero
  651.             try
  652.             {
  653.                 currIndex = 0;
  654.                 currMessage = messageList[0];
  655.             }
  656.             catch(ArrayIndexOutOfBoundsException e2)
  657.             {
  658.                 //No index is valid at this point
  659.                 currMessage = "";
  660.             }
  661.         }
  662.  
  663.         if(currIndex < linkToList.length)
  664.         {
  665.             currLinkTo = linkToList[currIndex];
  666.         }
  667.         else
  668.         {
  669.             currLinkTo = null;
  670.         }
  671. /*
  672.         // Get current link to, if one is there
  673.         try
  674.         {
  675.             currLinkTo = linkToList[currIndex];
  676.         }
  677.         catch(ArrayIndexOutOfBoundsException e)
  678.         {
  679.             currLinkTo = null;
  680.         }
  681. */
  682.         createTextParams();
  683.     }
  684.  
  685.     /**
  686.      * Increment scroll step.  Moves to next message as needed. Not usually called directly.
  687.      */
  688.     protected synchronized void nextPos()
  689.     {
  690.         Dimension dim = size();
  691.         if (scrollDirection == SCROLL_LEFT)
  692.         {
  693.             textX -= scrollUnit;
  694.             if ((textX + textWidth) < 0)
  695.             {
  696.                 updateCurrentMessage(true);
  697.                 textX = dim.width;
  698.             }
  699.         }
  700.         else
  701.         {
  702.             textX += scrollUnit;
  703.             if (textX > dim.width)
  704.             {
  705.                 updateCurrentMessage(true);
  706.                 textX = -textWidth;
  707.             }
  708.         }
  709.  
  710.         repaint();
  711.     }
  712.  
  713.     /**
  714.      * Setup metrics information for current message. Sets textX, textY, textHeight and textWidth.
  715.      */
  716.     protected void createTextParams()
  717.     {
  718.         Font f = getFont();
  719.         if (f != null)
  720.         {
  721.             FontMetrics fm = getFontMetrics(f);
  722.             textHeight = fm.getHeight();
  723.  
  724.             Dimension dim = size();
  725.             textX = dim.width;
  726.             textY = ((dim.height - textHeight) >> 1) + fm.getAscent();
  727.             textWidth = fm.stringWidth(currMessage);
  728.         }
  729.     }
  730.  
  731.     /**
  732.      * Handles redrawing of this component on the screen.
  733.      * This is a standard Java AWT method which gets called by the Java
  734.      * AWT (repaint()) to handle repainting this component on the screen.
  735.      * The graphics context clipping region is set to the bounding rectangle
  736.      * of this component and its <0,0> coordinate is this component's
  737.      * top-left corner.
  738.      * Typically this method paints the background color to clear the
  739.      * component's drawing space, sets graphics context to be the foreground
  740.      * color, and then calls paint() to draw the component.
  741.      *
  742.      * It is overridden here to reduce flicker by eliminating the uneeded
  743.      * clearing of the background.
  744.      *
  745.      * @param g the graphics context
  746.      * @see java.awt.Component#repaint
  747.      * @see #paint
  748.      */
  749.     public void update(Graphics g) {
  750.         paint(g);
  751.     }
  752.  
  753.     /**
  754.      * Determines if the given point is over the current text.
  755.      *
  756.      * @param x horizontal location of point
  757.      * @param y vertical location of point
  758.      *
  759.      * @return true if given point is over the current text
  760.      */
  761.     protected boolean isMouseOverText(int x, int y)
  762.     {
  763.         if (isMouseOver)
  764.         {
  765.             if (x >= textX && x <= textX + textWidth)
  766.             {
  767.                 if (y >= textY - textHeight && y <= textY)
  768.                     return true;
  769.             }
  770.         }
  771.  
  772.         return false;
  773.     }
  774.  
  775.     /**
  776.      * Paints this component using the given graphics context.
  777.      * This is a standard Java AWT method which typically gets called
  778.      * by the AWT to handle painting this component. It paints this component
  779.      * using the given graphics context. The graphics context clipping region
  780.      * is set to the bounding rectangle of this component and its <0,0>
  781.      * coordinate is this component's top-left corner.
  782.      *
  783.      * @param g the graphics context used for painting
  784.      * @see java.awt.Component#repaint
  785.      * @see #update
  786.      */
  787.     public void paint(Graphics g)
  788.     {
  789.         Dimension dim = size();
  790.         if(isImageInvalid())
  791.         {
  792.             textImage = createImage(dim.width, dim.height);
  793.             try
  794.             {
  795.                 MediaTracker tracker = new MediaTracker(this);
  796.                 tracker.addImage(textImage, 0);
  797.                 tracker.waitForID(0);
  798.             }
  799.             catch(InterruptedException e){}
  800.         }
  801.  
  802.         Graphics textGC = textImage.getGraphics();
  803.  
  804.         //Draw the background color
  805.         textGC.setColor(getBackground());
  806.         textGC.fillRect(0, 0, dim.width, dim.height);
  807.  
  808.         textGC.setFont(getFont());
  809.  
  810.         //Determine where the mouse is
  811.         boolean mouseOverText = isMouseOverText(lastMouseX, lastMouseY);
  812.         if (mouseOverText != wasMouseOverText)
  813.         {
  814.             if (context != null)
  815.             {
  816.                 String newStatusMessage;
  817.                 if (wasMouseOverText || currLinkTo == null)
  818.                     newStatusMessage = "";
  819.                 else
  820.                     newStatusMessage = currLinkTo.toString();
  821.  
  822.                 //Update status, only if something has changed
  823.                 if (!wasStatusMessage.equals(newStatusMessage))
  824.                 {
  825.                     context.showStatus(newStatusMessage);
  826.                     wasStatusMessage = newStatusMessage;
  827.                 }
  828.             }
  829.  
  830.             wasMouseOverText = mouseOverText;
  831.         }
  832.  
  833.         //Draw the text
  834.         textGC.setColor(mouseOverText && currLinkTo != null ? hiliteColor : getForeground());
  835.         textGC.drawString(currMessage, textX, textY);
  836.         g.drawImage(textImage, 0, 0, this);
  837.         if (textGC != null)
  838.             textGC.dispose();
  839.     }
  840.  
  841.     class MouseMotion extends MouseMotionAdapter implements java.io.Serializable
  842.     {
  843.         //???RKM??? Revist JavaDoc
  844.         /**
  845.          * Processes MOUSE_MOVE events.
  846.          * This is a standard Java AWT method which gets called by the AWT
  847.          * method handleEvent() in response to receiving a MOUSE_MOVE
  848.          * event. These events occur when the mouse is moved around inside this
  849.          * component while the button is NOT pressed.
  850.          *
  851.          * @param evt the event
  852.          * @param x the component-relative horizontal coordinate of the mouse
  853.          * @param y the component-relative vertical coordinate of the mouse
  854.          *
  855.          * @return always true since the event was handled
  856.          *
  857.          * @see java.awt.Component#mouseDrag
  858.          * @see java.awt.Component#handleEvent
  859.          */
  860.         public void mouseMoved(MouseEvent event) {
  861.             isMouseOver = true;
  862.  
  863.             //Use this later to determine if the mouse is over the text
  864.             lastMouseX = event.getX();
  865.             lastMouseY = event.getY();
  866.         }
  867.     }
  868.  
  869.     class Mouse extends MouseAdapter implements java.io.Serializable
  870.     {
  871.         /**
  872.          * Processes MOUSE_EXIT events.
  873.          * This is a standard Java AWT method which gets called by the AWT
  874.          * method handleEvent() in response to receiving a MOUSE_EXIT
  875.          * event. These events occur when the mouse first leaves this
  876.          * component.
  877.          *
  878.          * @param e the event
  879.          * @param x the component-relative horizontal coordinate of the mouse
  880.          * @param y the component-relative vertical coordinate of the mouse
  881.          *
  882.          * @return always true since the event was handled
  883.          *
  884.          * @see java.awt.Component#mouseEnter
  885.          * @see java.awt.Component#handleEvent
  886.          */
  887.         public void mouseExited(MouseEvent evt) {
  888.             isMouseOver = false;
  889.         }
  890.  
  891.         /**
  892.          * Processes MOUSE_DOWN events.
  893.          * This is a standard Java AWT method which gets called by the AWT
  894.          * method handleEvent() in response to receiving a MOUSE_DOWN
  895.          * event. These events occur when the mouse button is pressed while
  896.          * inside this component.
  897.          *
  898.          * @param e the event
  899.          * @param x the component-relative horizontal coordinate of the mouse
  900.          * @param y the component-relative vertical coordinate of the mouse
  901.          *
  902.          * @return always true since the event was handled
  903.          *
  904.          * @see java.awt.Component#mouseUp
  905.          * @see java.awt.Component#handleEvent
  906.          */
  907.         public void mousePressed(MouseEvent evt) {
  908.             //If we have somewhere to go and the mouse is over the text
  909.             if (currLinkTo != null && isMouseOverText(evt.getX(), evt.getY()))
  910.             {
  911.                 if (context != null)
  912.                 {
  913.                     if (frame == null || frame.length() == 0)
  914.                         context.showDocument(currLinkTo);
  915.                     else
  916.                         context.showDocument(currLinkTo,frame);
  917.                 }
  918.             }
  919.         }
  920.     }
  921.  
  922.     /**
  923.      * Ensures that this component is laid out properly, as needed.
  924.      * This is a standard Java AWT method which gets called by the AWT to
  925.      * make sure this component and its subcomponents have a valid layout.
  926.      * If this component was made invalid with a call to invalidate(), then
  927.      * it is laid out again.
  928.      *
  929.      * It is overridden here to also find the containing applet at validation time.
  930.      *
  931.      * @see java.awt.Component#invalidate
  932.      */
  933.     public void validate()
  934.     {
  935.         // On validation, try to find the containing applet.  If we can find
  936.         // it, we don't bother doing the link...
  937.         Container c = getParent();
  938.  
  939.         while (c != null)
  940.         {
  941.             if (c instanceof Applet)
  942.             {
  943.                 setAppletContext(((Applet) c).getAppletContext());
  944.                 break;
  945.             }
  946.  
  947.             c = c.getParent();
  948.         }
  949.     }
  950.  
  951.     /**
  952.      * Specify the current applet context.
  953.      *
  954.      * @param c new applet context
  955.      */
  956.      //???RKM??? Make certain this doesn't show up as a property
  957.     protected void setAppletContext(AppletContext c)
  958.     {
  959.         context = c;
  960.     }
  961.  
  962.     /**
  963.      * Moves and/or resizes this component.
  964.      * This is a standard Java AWT method which gets called to move and/or
  965.      * resize this component. Components that are in containers with layout
  966.      * managers should not call this method, but rely on the layout manager
  967.      * instead.
  968.      *
  969.      * @param x horizontal position in the parent's coordinate space
  970.      * @param y vertical position in the parent's coordinate space
  971.      * @param width the new width
  972.      * @param height the new height
  973.      */
  974.     public synchronized void reshape(int x, int y, int width, int height) {
  975.         super.reshape(x,y,width,height);
  976.  
  977.         createTextParams();
  978.     }
  979.  
  980.     /**
  981.      * Adds a listener for all event changes.
  982.      * @param PropertyChangeListener listener the listener to add.
  983.      * @see #removePropertyChangeListener
  984.      */
  985.     public void addPropertyChangeListener(PropertyChangeListener listener)
  986.     {
  987.         //super.addPropertyChangeListener(listener);
  988.         changes.addPropertyChangeListener(listener);
  989.     }
  990.  
  991.     /**
  992.      * Removes a listener for all event changes.
  993.      * @param PropertyChangeListener listener the listener to remove.
  994.      * @see #addPropertyChangeListener
  995.      */
  996.     public void removePropertyChangeListener(PropertyChangeListener listener)
  997.     {
  998.         //super.removePropertyChangeListener(listener);
  999.         changes.removePropertyChangeListener(listener);
  1000.     }
  1001.  
  1002.     /**
  1003.      * Adds a vetoable listener for all event changes.
  1004.      * @param VetoableChangeListener listener the listener to add.
  1005.      * @see #removeVetoableChangeListener
  1006.      */
  1007.     public void addVetoableChangeListener(VetoableChangeListener listener)
  1008.     {
  1009.          //super.addVetoableChangeListener(listener);
  1010.         vetos.addVetoableChangeListener(listener);
  1011.     }
  1012.  
  1013.     /**
  1014.      * Removes a vetoable listener for all event changes.
  1015.      * @param VetoableChangeListener listener the listener to remove.
  1016.      * @see #addVetoableChangeListener
  1017.      */
  1018.     public void removeVetoableChangeListener(VetoableChangeListener listener)
  1019.     {
  1020.         //super.removeVetoableChangeListener(listener);
  1021.         vetos.removeVetoableChangeListener(listener);
  1022.     }
  1023.  
  1024.     /**
  1025.      * Returns true if a image has been set, but it is not the
  1026.      * size of this component.
  1027.      */
  1028.     protected boolean isImageInvalid()
  1029.     {
  1030.         Dimension s = getSize();
  1031.         return (textImage == null || s.width     != textImage.getWidth(this) || s.height != textImage.getHeight(this));
  1032.     }
  1033.  
  1034.     /**
  1035.      *    The offscreen buffer to draw in.
  1036.      */
  1037.     transient protected Image    textImage = null;
  1038.  
  1039.     private symantec.itools.beans.VetoableChangeSupport vetos;
  1040.     private symantec.itools.beans.PropertyChangeSupport changes;
  1041.     private Mouse mouse = null;
  1042.     private MouseMotion mouseMotion = null;
  1043. }
  1044.